﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using SAM.Framework.Repository.Repositories;
using SAM.Framework.Repository.Entities;
using Dapper;
using Dapper.Contrib.Extensions;
using SAM.Framework.Repository.Connection;
using System.Data.Common;
using System.Data;
using System.Reflection;
using SAM.Framework.Repository.Attributes;
using SQLinq;
using System.Linq.Expressions;

namespace SAM.Framework.RepositoryDapper.NET40
{

    public class SQLDataBase : Database<SQLDataBase>
    { 
    }

    public partial class DapperRepositoryBase<TEntity> : RepositoryBase<TEntity, int>
        where TEntity : class, IEntity<int>
    {
        private BaseDbContext DbContext;
        public Database<SQLDataBase>.Table<TEntity> table;
        private SQLDataBase dataBase;
        protected ISqlDialect SqlDialect;

        public DapperRepositoryBase(BaseDbContext dbContext, string likelytablename)
        {
            this.DbContext = dbContext;
            this.dataBase = SQLDataBase.Init(this.DbContext.DbConnection, 10);
            this.table = new Database<SQLDataBase>.Table<TEntity>(dataBase, likelytablename);
            var tableNameProperty = this.table.GetType().GetField("tableName", BindingFlags.NonPublic | BindingFlags.Instance);
            if (tableNameProperty != null)
            {
                tableNameProperty.SetValue(this.table, likelytablename);
            }
        }
        public DapperRepositoryBase(BaseDbContext dbContext, string likelytablename,ISqlDialect sqlDialect)
        {
            this.DbContext = dbContext;
            this.dataBase = SQLDataBase.Init(this.DbContext.DbConnection, 10);
            this.table = new Database<SQLDataBase>.Table<TEntity>(dataBase, likelytablename);
            var tableNameProperty = this.table.GetType().GetField("tableName", BindingFlags.NonPublic | BindingFlags.Instance);
            if (tableNameProperty != null)
            {
                tableNameProperty.SetValue(this.table, likelytablename);
            }
            this.SqlDialect = sqlDialect;
        }
        public virtual DbConnection Connection
        {
            get
            {
                return this.DbContext.DbConnection;
            }
        }

        internal static List<string> GetParamNames(object o)
        {
            if (o is DynamicParameters)
            {
                return (o as DynamicParameters).ParameterNames.ToList();
            }

            List<string> paramNames;
            {
                paramNames = new List<string>();
                foreach (var prop in o.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public))
                {
                    var attribs = prop.GetCustomAttributes(typeof(IgnorePropertyAttribute), true);
                    var attr = attribs.FirstOrDefault() as IgnorePropertyAttribute;
                    if (attr == null || (attr != null && !attr.Value))
                    {
                        paramNames.Add(prop.Name);
                    }
                }
            }
            return paramNames;
        }

        public override void Delete(TEntity entity)
        {
            this.Delete(entity.Id);
        }


        public override IQueryable<TEntity> GetAll()
        {
            
            return this.table.All().AsQueryable<TEntity>();
        }

        public override TEntity Insert(TEntity entity)
        {
            var o = (object)entity;
            List<string> paramNames = GetParamNames(o);

            string cols = string.Join(",", paramNames);
            string cols_params = string.Join(",", paramNames.Select(p => "@" + p));
            var sql = "set nocount on insert " + this.table.TableName + " (" + cols + ") values (" + cols_params + ") select cast(scope_identity() as int)";

            var lastId = this.dataBase.Query<int?>(sql, o).Single();
            if (lastId.HasValue)
            {
                entity.Id = lastId.Value;
            }
            return entity;
        }

        public override TEntity Update(TEntity entity)
        {
            this.table.Update(entity.Id, entity);
            return entity;
        }
       

        public override void Delete(int id)
        {
            this.table.Delete(id);
        }
        public override TEntity FirstOrDefault(int id)
        {
            var linq = from a in new SQLinq.SQLinq<TEntity>().Where(w => w.Id == id).Take(1) select a;
            var query = linq.ToSQL();
            var param = query.Parameters;
            return this.QuerySql<TEntity>(query.ToQuery(),param).FirstOrDefault();
        }

        public override IEnumerable<T> QuerySql<T>(string sql, IDictionary<string, object> param)
        {
            return this.dataBase.Query<T>(sql,param);
        }

        public override int QueryCount(string sql)
        {
            var rec = this.dataBase.Query<Repository.CustomCount>(sql).FirstOrDefault();
            if (rec != null)
            {

                return rec.Count;
            }
            else
            {
                return 0;
            }
        }

        public override int QueryCount(string sql, IDictionary<string, object> param)
        {
            var result = this.dataBase.Query<Repository.CustomCount>(sql,param);
            var rec = result.FirstOrDefault();
            if (rec != null)
            {
                
                return rec.Count;
            }
            else {
                return 0;
            }
        }

        public override IEnumerable<TEntity> QueryLinq(Expression<Func<TEntity, bool>> predicate)
        {
            var linq = from a in new SQLinq.SQLinq<TEntity>().Where(predicate) select a;
            var query = linq.ToSQL();
            var param = query.Parameters;
            return QuerySql<TEntity>(query.ToQuery(), param);
        }

        public override IEnumerable<T> QuerySql<T>()
        {
            var linq = from a in new SQLinq.SQLinq<T>() select a;
            var sql = linq.ToSQL().ToQuery();
            return QuerySql<T>(sql, null);
        }

        public override int BaseCount(Expression<Func<TEntity, bool>> predicate)
        {
            var linq = (from a in new SQLinq.SQLinq<TEntity>().Where(predicate) select a).Count();
            var query = linq.ToSQL();
            var param = query.Parameters;
            return QueryCount(query.ToQuery().Replace("COUNT(1)", " COUNT(1) AS COUNT "), param);
        }

        public override int BaseCount()
        {
            var linq = (from a in new SQLinq.SQLinq<TEntity>() select a).Count();
            var sql = linq.ToSQL().ToQuery();
            sql = sql.Replace("COUNT(1)", " COUNT(1) AS COUNT ");
            return QueryCount(sql, null);
        }
        /*
        public IEnumerable<T> QueryCustom<T,A,B,C>(SQLinq<A> a,SQLinq<B> b,SQLinq<C> c,Expression<Func<A,B,C,bool>> predicate)
        {
            var linqa = a.Where(predicate).ToSQL();
            var linqb = b.Where(predicate).ToSQL();
            var linqc = c.Where(predicate).ToSQL();
            var resultA = QuerySql<A>(linqa.ToQuery(), linqa.Parameters);
            var resultB = QuerySql<B>(linqb.ToQuery(), linqb.Parameters);
            var resultC = QuerySql<C>(linqc.ToQuery(), linqc.Parameters);
            return null;
        }
        */
    }
}
